/*
 * @Author: yuqy
 * @Date: 2019-08-13 14:07:40
 * @Last Modified by: gulin
 * @Last Modified time: 2021-08-02 13:23:00
 */
/* eslint-disable */
// 基于上行发送码率、上行丢包率、平均往返时延和网络抖动计算。
// 0: 质量未知。
// 1: 质量极好。
// 2: 用户主观感觉和极好差不多，但码率可能略低于极好。
// 3: 用户主观感受有瑕疵但不影响沟通。
// 4: 勉强能沟通但不顺畅。
// 5: 网络质量非常差，基本不能沟通。
// 6: 网络连接断开，完全无法沟通。
import { mapState, mapGetters } from 'vuex'
import { Message } from 'view-ui-plus';
import AgoraRTC from "agora-rtc-sdk-ng";
// 当前页面
let pageName = null
// 本地播放框子
let showLocalVideoEl = 'homeSpeaker_local_s_video_wrap'
// 听讲播放主讲的框子
let showTeacherVideoEl = 'listenSpeaker_live_video'
export default {
    data() {
        return {
            uid: '', // 用户声网推流标识
            rtcClient: null,
            localVideoTrack: null,
            localAudioTrack: null,
            roomVolumes: null,
            mode: 'live', // 默认是直播
            role: 'host', // 标记使用用户类型 host是讲师，audience是听众
        }
    },
    created() {
        pageName = this.$route.name;
        // 主讲的讲师取手机号后4位,然后加上前缀加2判断
        this.uid = parseInt(2 + this.uid_);
    },
    mounted() { },
    unmounted() {
        this.closeClient()
    },
    computed: {
        ...mapState({
            token: (state) => state.roomAgora.agoraToken,
            appid: (state) => state.roomAgora.appid,
            channelName: (state) => state.roomAgora.channelName,
            deviceVideoId: (state) => state.roomAgora.deviceVideoId
        }),
        ...mapGetters({
            getDemo: 'roomAgora/getDemo',
            uid_: 'user/getUserUid',
            lowVideoEncoder: 'roomAgora/getLowVideoEncoder',
            highVideoEncoder: 'roomAgora/getHighVideoEncoder',
            audioencoderConfig: 'roomAgora/getAudioencoderConfig',
        })
    },
    mounted() {

    },
    methods: {
        // 听讲取手机号后4位,然后加上前缀加1判断
        getListenUid() {
            this.uid = parseInt(1 + this.uid_);
        },
        /**
         * @name: 为了适配声网采集桌面需求 ，需要重置相关方法
         * @msg:
         * @return {*}
         */
        async resetGetScreen() {

        },
        /**
         * @name: 选中麦克风采集设备
         * @msg: 
         * @param {*} list
         * @param {*} index
         * @return {*}
         */
        async actionSheetList({ deviceId }, index) {

        },
        /**
         * @name: 选中摄像头采集设备
         * @msg: 
         * @return {*}
         */
        async actionCameraSheetList({ deviceId }, index) {

        },
        async setWebCameraId() {

        },
        /**
         * @name: 获取音频采集设备列表 electron 和 网页需要区分开来
         * @msg: 
         * @return {*}
         */
        async getAllMicrophoneList() {

        },

        /**
         * @name: 获取摄像头设备列表 electron 和 网页需要区分开来
         * @msg: 
         * @return {*}
         */
        async getAllCameraList() {

        },
        /**
         * @name: 网页端获取 摄像头列表
         * @msg: 
         * @return {*}
         */
        async getAllCameraWebList() {

        },
        /**
         * @name: 用户通过客户端按钮，直接退出房间
         * @msg: 
         * @return {*}
         */
        userOutRoomClient() {

        },
        /**
         * @name: 获取声网采集参数
         * @msg: 
         * @return {*}
         */
        getVideoEncoder(label) {

        },
        /**
         * @name: 渲染解析视频流
         * @msg: 
         * @param {*} item
         * @return {*}
         */
        renderVideo() { },
        /**
         * @name: 检测声网客户端，有没有启动
         * @msg: 
         * @return {*}
         */
        isCheckClientRun() {

        },
        showConsole() {

        },
        /**
         * @name: 主讲老师主动去拉取听众列表视频 -------------------------------- ----------------------
         * @msg: 
         * @param {*} mode
         * @return {*}
         */
        async initAgoraList() {

        },
        /**
         * @name: 主讲页面监控用户列表在房间内推入视频流事件
         * @msg: 
         * @param {*} user
         * @param {*} mediaType
         * @return {*}
         */
        async homeuserPublished(user, mediaType) {

        },
        async homeuserUnPublished(user, mediaType) {

        },
        /**
         * @name:  初始化声网客户端
         * @msg: 
         * @param {*} mode
         * @return {*}
         */
        async initAgora() {
            if (this.rtcClient !== null) return
            let uid = this.uid * 1
            let mode = this.mode
            // 使用客户端推流视频，不能使用同一个uid去监控,原始uid传给了客户端用于推流, 该client只当一个观察者
            if (!this.APP_IS_Web) {
                uid = this.uid * 10
                // mode = 'rtc'
            }
            // 禁止打印日志
            AgoraRTC.setLogLevel(4)
            // 直播使用 mode必须是live
            this.rtcClient = AgoraRTC.createClient({ mode: 'live', codec: "h264" })
            // this.rtc.client = AgoraRTC.createClient({ mode, codec: "h264" })
            // this.rtc.client = AgoraRTC.createClient({ mode, codec: "vp8" })
            // 设置观众角色
            if (mode == 'live') {
                // 如果是客户端采流，网页端全部是默认听讲角色
                if (this.role == 'audience') { // 听讲角色
                    this.rtcClient.setClientRole(this.role, { level: this.audienceLatency });
                } else {
                    this.rtcClient.setClientRole(this.role);
                }
            }
            // uid 大于20000 * 10
            await this.rtcClient.join(this.appid, this.channelName, this.token, uid)
            // 监听用户加入
            this.rtcClient.on("user-published", this.userPublished)
            this.rtcClient.on("user-unpublished", this.userUnPublished)
            // 实时检测token是否失效
            this.rtcClient.on("token-privilege-will-expire", this.getNewToken)
            // 实时检测用户是否离开
            this.rtcClient.on("user-left", this.userLeft)
            // 启用说话者音量提示。
            this.rtcClient.enableAudioVolumeIndicator();
            this.rtcClient.on("volume-indicator", volumes => {
                this.roomVolumes = volumes
            })
            // 实时监控频道内发生异常的音视流
            this.rtcClient.on("exception", this.exceptionErrorDialog)
            // 上下行网速
            this.rtcClient.on("network-quality", (stats) => {
                this.networkQuality = stats;
            });
            // 开始调用推流方法
            this.startGetPullVideo()
            // 听讲用户10秒后，不管成功与否，都要往主讲推送一条信息
            if (this.uid >= 20000) return
            setTimeout(() => {
                this.sendCommonMsg('401')
            }, 1000 * 10)
        },
        /**
         * @name: 针对频道内的异常数据进行监控提示
         * @msg: 
         * @return {*}
         */
        exceptionErrorDialog({ code, uid }) {

        },
        /**
         * @name: 开始推送视频流
         * @msg: 
         * @return {*}
         */
        startGetPullVideo() {
            // 主讲页面 视频音频全推送
            if (pageName == 'homeSpeaker') {
                this.getLocalVideoAudio(2);
            }
            // 听讲页面 只推送视频
            if (pageName == 'listenSpeaker') {
                this.getLocalVideoAudio();
            }
            // 推送超低清晰度副流
            // setTimeout(() => {
            //   this.getHomeSpeakerSecond()
            // }, 500)
        },
        /**
         * @name: 正常离开：用户会收到类似“再见”的消息，接收此消息后，判断对方离开频道。
                  超时掉线：在 20 秒内，用户没有收到对方的任何数据包，则判定为对方掉线。在网络较差的情况下，有可能会误报。
                  用户角色从主播变为观众。
         * @msg: 
         * @return {*}
         */
        userLeft(user, reason) {

        },
        /**
         * @name: 有用户退出课堂了
         * @msg: 
         * @param {*} uid
         * @return {*}
         */
        someUserOut({ uid }, reason) {

        },
        /**
         * @name: 实时获取新token并替换
         * @msg: 
         * @return {*}
         */
        async getNewToken() {
            Message.error('声网token已过期，请退出后重试!');
            setTimeout(() => {
                // 客户端直接退出当前页面，并关闭客户端
                if (!this.APP_IS_Web) {
                    window.electronAPI.resize('close', {})
                }
                // 直接退出本页面，回到主页
                this.$router.push('./index')
            }, 1000 * 10)
            // 更换token
            //await this.rtcClient.renewToken(token)
        },
        /**
         * @name: 展示当前正在与主讲互动的听讲
         * @msg: 
         * @param {*} user
         * @param {*} mediaType
         * @return {*}
         */
        async showOtherOnSpeakerUser(user, mediaType) {

        },
        async otherOnSpeakerUserPlay(user_) {

        },
        /**
         * @name: 监控用户在房间内推入视频流事件(暂时不使用)
         * @msg: 
         * @param {*} user
         * @param {*} mediaType
         * @return {*}
         */
        async showOtherUser(user, mediaType) {

        },
        /**
         * @name: 订阅主讲老师 并播放
         * @msg: 
         * @return {*}
         */
        subscribeMainTeavher(user) {
            const remoteVideoTrack = user.videoTrack;
            remoteVideoTrack.play(`${showTeacherVideoEl}`);
        },
        /**
         * @name: 监控用户在房间内推入视频流事件
         * @msg: 
         * @param {*} user
         * @param {*} mediaType
         * @return {*}
         */
        async userPublished(user, mediaType) {
            // 主动过滤自己本身的音视频，不播放也不要订阅
            if (this.uid * 10 == user.uid || this.uid == user.uid) return
            if (pageName == 'homeSpeaker') return
            if (user.uid >= 20000) return
            // Subscribe to a remote user.
            await this.rtc.client.subscribe(user, mediaType)
            // // 页面内有播放容器的情况下，才需要播放\
            if (mediaType === "video") {
                this.subscribeMainTeavher(user)
            }
            // 听讲端需要播放主讲老师的音视频
            if (mediaType === "audio") {
                user.audioTrack.play();
            }
        },
        async userUnPublished(user, mediaType) {

        },
        /**
         * @name: 
         * @msg: 
         * @param {*} type true 开启  false关闭
         * @return {*}
         */
        startGetScreen(type) {
            // window.enableClassroomScreen(true) // 开启采集桌面
            // window.enableClassroomScreen(false) // 关闭采集桌面
        },
        /**
         * @name: 使用eletron,进行视频采集
         * @msg: type 0 导播  1本地摄像头 2本地桌面
         *   videoType 1单视频，2是视频 + 声音, 3 是单独采集音频 
         * 客户端采集需要初始化的时候打开声音采集
         * @return {*}
         */
        async getVideoEletronClient(videoType = 1) {

        },

        /**
         * @name: eletron加入声网客户端
         * @msg: 
         * @param {*} options
         * @return {*}
         */
        async joinEletronAgora(options) {

        },

        /**
         * @name: 客户端停止
         * @msg: 
         * @return {*}
         */
        eletronAgoraLeave() {

        },
        /**
         * @name: 开始采集声网，
         * @msg: type : screen 桌面, camera 摄像头, window 导播
         * @return {*}
         */
        async getAgoraVideo(options) {

        },
        /**
         * @name: 获取所有可用的桌面进程，仅仅是在eletron环境下才可以
         * @msg: 
         * @return {*}
         */
        async getAllScreenList() { },
        /**
         * @name: 主讲采集一路副流 采集原则为，主流是桌面 就采集一路摄像头，或者反之一路桌面, 而且只采集视频不采集音频
         * @msg: 只有客户端才能同时推送副流，网页禁止推送副流
         * @return {*}
         */
        async getHomeSpeakerSecond() {

        },
        /**
         * @name: 采集本地屏幕 需要用户手动设置录播地址
         * @msg: type 1单视频听讲端， 2是视频 + 声音  主讲端
         * @return {*}
         */
        async getLocalVideoAudio(type = 1) {
            // electron 客户端采流， 调取客户端采流方法
            if (!this.APP_IS_Web) {
                return this.getVideoEletronClient(type)
            }
            switch (type) {
                case 4: // 只采集本地桌面
                    await this.getLocalScreenTrack()
                    if (pageName == 'homeSpeaker') {
                        this.getLocalAudioTrack()
                    }
                    break;
                case 2: // 采集本地usb摄像头和本地音频 
                    this.getLocalCameraAudioTrack();
                    break;
                case 1: // 单独抓取摄像头
                    this.getLocalCameraTrack();
                    break;
                default:
                    break;
            }
        },
        /**
         * @name: 获取本地桌面视频流 并推送 ======================================核心=========
         * @msg: 
         * @return {*}
         */
        async getLocalScreenTrack() {
            try {
                if (this.localAudioTrack) {
                    await this.rtcClient.unpublish(this.localAudioTrack)
                }
                const localVideoTrack = await AgoraRTC.createScreenVideoTrack({
                    optimizationMode: 'motion', // "detail": （默认）清晰优先。 "motion": 流畅优先。
                    encoderConfig: this.highVideoEncoder, // 采集信息码率，和帧率最高级别  可以直接输入1080p_2
                })
                // 将本地视频显示到小窗口
                if (pageName == 'homeSpeaker') {
                    this.showLocalVideo(localVideoTrack)
                }
                this.rtcClient.publish(localVideoTrack);
                this.localVideoTrack = localVideoTrack;
            } catch (error) {
                this.AgoraRTCError(error);
            }
        },
        /**
         * @name: 采集本地音频进行发言 =======================  采集音频 =============================== 核心
         * @msg: 
         * @return {*}
         */
        async getLocalAudioTrack() {
            const hasAudio = await this.checkAudio()
            // 检测输入音频设备
            if (!hasAudio) return
            try {
                if (this.localAudioTrack) {
                    await this.rtcClient.unpublish([this.localAudioTrack])
                }
                // AEC回声消除 AGC 自动增益   ANS 噪声抑制
                const localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack({
                    AEC: true, AGC: false, ANS: true,
                    encoderConfig: this.audioEncoderConfig
                })
                // console.log('开始推送音频' + new Date().getSeconds())
                // 主动将本地麦克风音量扩大一倍
                localAudioTrack.setVolume(200);
                // 将本地音频流推到服务器
                await this.rtcClient.publish([localAudioTrack]);
                this.localAudioTrack = localAudioTrack
            } catch (error) {
                this.AgoraRTCError(error);
            }
        },
        /**
         * @name: 获取本地摄像头视频流加上本地的音频流 并推送 ======================================核心=========
         * @msg: 
         * @return {*}
         */
        async getLocalCameraAudioTrack() {
            const hasAudio = await this.checkAudio()
            // 检测输入音频设备
            if (!hasAudio) return this.getLocalCameraTrack()
            try {
                if (this.localAudioTrack) {
                    await this.rtcClient.unpublish(this.localAudioTrack)
                }
                // detail": 清晰优先。 "motion": 流畅优先。 // user 采集前置摄像头
                const localVideoAudioTrack = await AgoraRTC.createMicrophoneAndCameraTracks(
                    {
                        encoderConfig: this.audioEncoderConfig,
                        AEC: true, AGC: false, ANS: true  // AEC回声消除 AGC 自动增益   ANS 噪声抑制
                    },
                    {
                        encoderConfig: this.highVideoEncoder,
                        optimizationMode: 'motion',
                        facingMode: 'user',
                        cameraId: this.deviceVideoId || ''
                    }
                )
                // 需要将本地USB视频显示到小窗口
                localVideoAudioTrack.forEach(videoTrack => {
                    if (videoTrack.trackMediaType == 'video') this.showLocalVideo(videoTrack)
                })
                this.rtcClient.publish(localVideoAudioTrack);
                this.localVideoTrack = localVideoAudioTrack;
            } catch (error) {
                this.AgoraRTCError(error);
            }
        },
        /**
         * @name: 获取本地摄像头视频流 并推送 ======================================核心=========
         * @msg: 
         * @return {*}
         */
        async getLocalCameraTrack() {
            try {
                if (this.localAudioTrack) {
                    await this.rtcClient.unpublish([this.localAudioTrack])
                }
                localAudioTrack = await AgoraRTC.createCameraVideoTrack({
                    cameraId: this.deviceVideoId || '',
                    encoderConfig: this.lowVideoEncoder, optimizationMode: 'motion', facingMode: 'user'
                });
                await this.rtcClient.publish([localAudioTrack]);
                this.localAudioTrack = localAudioTrack;
            } catch (error) {
                this.AgoraRTCError(error);
            }
        },
        /**
         * @name: 展示本地视频
         * @msg: 
         * @return {*}
         */
        showLocalVideo(videoTrack) {
            videoTrack.play(`${showLocalVideoEl}`);
        },
        /**
         * @name: 声网异常处理
         * @msg: 
         * @return {*}
         */
        AgoraRTCError(error) {
            let desc = error.code || error.name || '';
            switch (error.code) {
                case 'NOT_READABLE':
                    desc = '请检查当前采集设备和驱动程序是否出错'
                    this.sendCommonMsg('404')
                    break;
                case 'PERMISSION_DENIED':
                    desc = '获取权限异常，请检查后重试。'
                    this.sendCommonMsg('404')
                    break;
                case 'UNEXPECTED_ERROR':
                    desc = '摄像头不可用'
                    if (pageName == 'listenSpeaker') this.getLocalScreenTrack(4)
                    break;
                default:
                    break;
            }
            Message.error(desc);
        },

        /**
         * @name: electron 客户端进行采集声音
         * @msg: 
         * @return {*}
         */
        async getLocalElectronAudio() {

        },
        /**
         * @name: 检测是否存在可用，视频录入设备
         * @msg: 
         * @return {*}
         */
        async checkVideo() {

        },
        /**
         * @name: 检测是否存在可用，音频录入设备 ----------------
         * @msg: 
         * @return {*}
         */
        async checkAudio() {
            const getMicrophones = await AgoraRTC.getMicrophones({ skipPermissionCheck: true })
            if (getMicrophones.length <= 0) {
                Message.error('未获取到音频输入设备,请检查授权情况');
            }
            return getMicrophones.length > 0
        },
        /**
         * @name: 中止音频采集 --------------------------
         * @msg: 
         * @return {*}
         */
        async stopGetAudio() {
            if (!this.APP_IS_Web) return this.stopLocalElectronAudio()
            // 音频采集类
            if (this.localAudioTrack) {
                await this.rtcClient.unpublish(this.localAudioTrack);
                this.localAudioTrack.stop();
                this.localAudioTrack.close();
                this.localAudioTrack = null;
            }
        },
        /**
         * @name: 中止视频采集 --------------------------
         * @msg: 
         * @return {*}
         */
        async stopGetVideo() {
            if (!this.APP_IS_Web) return this.stopLocalElectronAudio()
            try {
                // 视频采集类
                if (Array.isArray(this.localVideoTrack)) {
                    this.localVideoTrack.forEach(track => {
                        if (track) {
                            track.stop();
                            track.close();
                            track = undefined;
                        }
                    })
                } else {
                    this.localVideoTrack.stop();
                    this.localVideoTrack.close();
                }
                this.localVideoTrack = null
            } catch (error) {
                console.log(error)
            }
        },
        /**
         * @name: electron 客户端停止采集声音
         * @msg: 
         * @return {*}
         */
        async stopLocalElectronAudio() {
            window.electronAPI.agora.stopGetAudio()
        },

        /**
         * @name: 关闭客户端
         * @msg: 
         * @return {*}
         */
        async closeClient() {
            if (!this.APP_IS_Web) this.eletronAgoraLeave()
            if (!this.rtcClient) return
            try {
                await this.stopGetVideo()
                await this.stopGetAudio()
                await this.rtcClient.unpublish()
                await this.rtcClient.leave()
                this.rtcClient = null
            } catch (error) {
                console.log(error)
            }
        },
        /**
         * @name: 停止推流, 重新进行采流推流操作
         * @msg: 
         * @return {*}
         */
        async reloadPublishVideo() {
        },
        /**
         * @name: 关闭electron里面的web客户端采集推流功能
         * @msg: 
         * @return {*}
         */
        async closeElectronWebClient() {
            if (this.rtc.localSecondVideoTrack) {
                this.rtc.localSecondVideoTrack.stop();
                this.rtc.localSecondVideoTrack.close();
            }
            await this.rtc.client.unpublish(this.rtc.localSecondVideoTrack)
            // await this.rtc.client.leave()
            this.rtc.localSecondVideoTrack = null
            // this.rtc.client = null
            // this.client = null
        },
        /**
         * @name: 获取主讲页面听讲列表的视频blob地址，提供给副屏使用
         * @msg: 
         * @return {*}
         */
        getListerUserVideoBlob() {
        },
        /**
         * @name: 实时刷新获取图片
         * @msg: 
         * @return {*}
         */
        getNowTimeListerImg(element, canvas) {
        },
        /**
         * @name: // base64转成blob
         * @msg: 
         * @param {*} dataurl
         * @return {*}
         */
        dataURLtoBlob(dataurl) {
            var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
                bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
            while (n--) {
                u8arr[n] = bstr.charCodeAt(n);
            }
            return new Blob([u8arr], { type: mime });
        }
    }
}
